﻿/*
 *  DFUtil - a utility for creating and implementing blueprints in Dwarf Fortress.
 *  
 *  Copyright (C) 2009  Katharine Gillis
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace DFUtil
{
    /// <summary>
    /// A custom blueprint editor class that allows the editing of DFUtil blueprints.
    /// </summary>
    public partial class BlueprintEditor : UserControl
    {
        #region Variables

        private List<Phase> _phases;
        private Point lastPoint;
        private bool _settingStartingPoint = false;

        #endregion

        #region Properties

        private string _currentItemType;
        /// <summary>
        /// Gets or sets the current item type.
        /// </summary>
        public string CurrentItemType
        {
            get { return _currentItemType; }
            set { _currentItemType = value; }
        }

        private int _activePhase;
        /// <summary>
        /// Gets or sets the active phase.
        /// </summary>
        public int ActivePhase
        {
            get { return _activePhase; }
            set
            {
                if (_phases[value].StartingFloor != 0)
                    grid1.StartingPoint = new Point(-1, -1);
                else
                    grid1.StartingPoint = _phases[value].StartingPoint;
                grid1.Clear();
                grid1.AddAll(_phases[value].Floors[0]);
                _activePhase = value;
                _activeFloor = 0;
            }
        }

        private int _activeFloor;
        /// <summary>
        /// Gets or sets the active floor.
        /// </summary>
        public int ActiveFloor
        {
            get { return _activeFloor; }
            set
            {
                if (_phases[_activePhase].StartingFloor != value)
                    grid1.StartingPoint = new Point(-1, -1);
                else
                    grid1.StartingPoint = _phases[_activePhase].StartingPoint;
                grid1.Clear();
                grid1.AddAll(_phases[_activePhase].Floors[value]);
                _activeFloor = value;
            }
        }

        /// <summary>
        /// Gets or sets the active phase's name.
        /// </summary>
        public string PhaseName
        {
            get { return _phases[_activePhase].Name; }
            set { _phases[_activePhase].Name = value; }
        }

        private string _fileName;
        /// <summary>
        /// Gets or sets the blueprint file name.
        /// </summary>
        public string FileName
        {
            get { return _fileName; }
            set { _fileName = value; }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Creates the blueprint editor control, creates a new designate phase, sets it as the active phase, and sets the
        /// current item type to be "d" (mine).
        /// </summary>
        public BlueprintEditor()
        {
            InitializeComponent();
            _currentItemType = "d";
            _phases = new List<Phase>();
            _phases.Add(new Phase(PhaseType.Designate));
            _activePhase = 0;
            _activeFloor = 0;
            grid1.StartingPoint = new Point(0, 0);
            _fileName = "Untitled";

            grid1.MouseDown += new MouseEventHandler(grid1_MouseDown);
            grid1.MouseMove += new MouseEventHandler(grid1_MouseMove);
            grid1.MouseUp += new MouseEventHandler(grid1_MouseUp);
        }

        #endregion

        #region Usercontrol event callback functions

        /// <summary>
        /// Handles the MouseUp event.
        /// </summary>
        /// <param name="sender">Object that sent the MouseUp event.</param>
        /// <param name="e">Mouse event arguments.</param>
        void grid1_MouseUp(object sender, MouseEventArgs e)
        {
            lastPoint = new Point(-1, -1);
        }

        /// <summary>
        /// Handles the MouseMove event.
        /// </summary>
        /// <param name="sender">Object that sent the MouseMove event.</param>
        /// <param name="e">Mouse event arguments.</param>
        void grid1_MouseMove(object sender, MouseEventArgs e)
        {
            Point p = new Point(e.X / grid1.GridWidth, e.Y / grid1.GridHeight);
            if (p != lastPoint)
                grid1_MouseDown(sender, e);
        }

        /// <summary>
        /// Handles the MouseDown event.
        /// </summary>
        /// <param name="sender">Object that sent the MouseDown event.</param>
        /// <param name="e">Mouse event arguments.</param>
        void grid1_MouseDown(object sender, MouseEventArgs e)
        {
            Point p = new Point(e.X / grid1.GridWidth, e.Y / grid1.GridHeight);
            if (e.Button == MouseButtons.Left)
            {
                if (!_settingStartingPoint)
                {
                    foreach (Item i in _phases[_activePhase].Floors[_activeFloor])
                    {
                        Rectangle intersection = Rectangle.Intersect(new Rectangle(p, new Size(1, 1)), new Rectangle(i.Point, i.Size));
                        if (intersection.Width != 0 && intersection.Height != 0)
                            return;
                    }

                    Item item = new Item(p, new Size(1, 1), _currentItemType);
                    _phases[_activePhase].Floors[_activeFloor].Add(item);
                    List<Item> newItems = LargestRectangleAlgorithm.Run(_phases[_activePhase].Floors[_activeFloor], item.Type);
                    for (int i = _phases[_activePhase].Floors[_activeFloor].Count - 1; i >= 0; i--)
                    {
                        if (_phases[_activePhase].Floors[_activeFloor][i].Type == item.Type)
                        {
                            _phases[_activePhase].Floors[_activeFloor].RemoveAt(i);
                        }
                    }

                    grid1.RemoveAll(item.Type);

                    foreach (Item newItem in newItems)
                    {
                        grid1.Add(newItem.Point, newItem.Size, newItem.Type);
                        _phases[_activePhase].Floors[_activeFloor].Add(newItem);
                    }
                }
                else
                {
                    _phases[_activePhase].StartingPoint = p;
                    _phases[_activePhase].StartingFloor = _activeFloor;
                    grid1.StartingPoint = p;
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                string type = null;
                foreach (Item i in _phases[_activePhase].Floors[_activeFloor])
                {
                    Rectangle intersection = Rectangle.Intersect(new Rectangle(p, new Size(1, 1)), new Rectangle(i.Point, i.Size));
                    if (intersection.Width != 0 && intersection.Height != 0)
                    {
                        _phases[_activePhase].Floors[_activeFloor].Remove(i);
                        type = i.Type;

                        if (_phases[_activePhase].Type == PhaseType.Designate || _phases[_activePhase].Type == PhaseType.Stockpile)
                        {
                            int x = i.Point.X;
                            while (x < i.Size.Width + i.Point.X)
                            {
                                int y = i.Point.Y;
                                while (y < i.Size.Height + i.Point.Y)
                                {
                                    Point point = new Point(x, y);
                                    if (point != p)
                                    {
                                        Item newItem = new Item(point, new Size(1, 1), i.Type);
                                        _phases[_activePhase].Floors[_activeFloor].Add(newItem);
                                    }
                                    y++;
                                }
                                x++;
                            }
                        }
                        break;
                    }
                }

                List<Item> newItems = LargestRectangleAlgorithm.Run(_phases[_activePhase].Floors[_activeFloor], type);
                for (int i = _phases[_activePhase].Floors[_activeFloor].Count - 1; i >= 0; i--)
                {
                    if (_phases[_activePhase].Floors[_activeFloor][i].Type == type)
                    {
                        _phases[_activePhase].Floors[_activeFloor].RemoveAt(i);
                    }
                }

                grid1.RemoveAll(type);

                foreach (Item newItem in newItems)
                {
                    grid1.Add(newItem.Point, newItem.Size, newItem.Type);
                    _phases[_activePhase].Floors[_activeFloor].Add(newItem);
                }
            }
            lastPoint = p;
        }

        /// <summary>
        /// Handles the Resize event.
        /// </summary>
        /// <param name="e">Resize event arguments.</param>
        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            if (grid1.Width < panel1.Width)
                grid1.Width = panel1.Width + 1;
            if (grid1.Height < panel1.Height)
                grid1.Height = panel1.Height + 1;
        }

        #endregion

        #region Members

        /// <summary>
        /// Adds a new phase to the blueprint of the given type, at the given index.
        /// </summary>
        /// <param name="type">Type of phase to add.</param>
        /// <param name="index">Index at which to add the phase.</param>
        public void AddPhase(PhaseType type, int index)
        {
            _phases.Insert(index, new Phase(type));
            _activeFloor = 0;
            ActivePhase = index;
        }

        /// <summary>
        /// Removes a phase from the blueprint at the given index, and updates the active phase if it needs to.
        /// </summary>
        /// <param name="index">Index of phase to remove.</param>
        public bool RemovePhase(int index)
        {
            if (_phases.Count > 1)
            {
                _phases.RemoveAt(index);
                if (ActivePhase == index)
                {
                    if (ActivePhase == _phases.Count)
                    {
                        ActivePhase--;
                    }
                    else
                    {
                        _activeFloor = 0;
                        grid1.Clear();
                        grid1.AddAll(_phases[ActivePhase].Floors[0]);
                    }
                }
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Gets a string list of the names of the phases.
        /// </summary>
        /// <returns>String list of phase names.</returns>
        public List<string> GetPhaseNames()
        {
            List<string> names = new List<string>();
            int i = 0;
            foreach (Phase phase in _phases)
            {
                i++;
                names.Add((phase.Name != null ? phase.Name : "Phase " + i));
            }
            return names;
        }

        /// <summary>
        /// Adds a floor to the active phase, and sets it as active.
        /// </summary>
        /// <param name="index"></param>
        public void AddFloor(int index)
        {
            _phases[_activePhase].Floors.Insert(index, new List<Item>());
            ActiveFloor = index;
        }

        /// <summary>
        /// Removes a floor from the active phase at the given index.
        /// </summary>
        /// <param name="index">Index of floor to remove.</param>
        public bool RemoveFloor(int index)
        {
            if (_phases[_activePhase].Floors.Count > 1)
            {
                _phases[_activePhase].Floors.RemoveAt(index);
                if (ActiveFloor == index)
                {
                    if (ActiveFloor == _phases[_activePhase].Floors.Count)
                    {
                        ActiveFloor--;
                    }
                    else
                    {
                        grid1.Clear();
                        grid1.AddAll(_phases[ActivePhase].Floors[_activeFloor]);
                    }
                }
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Gets a string list of floor names for the active phase.
        /// </summary>
        /// <returns>String list of floor names.</returns>
        public List<string> GetFloorNames()
        {
            List<string> names = new List<string>();
            names.Add("Main Floor");
            for (int i = 1; i < _phases[_activePhase].Floors.Count; i++)
            {
                names.Add("Floor " + i);
            }
            return names;
        }

        /// <summary>
        /// Toggle whether or not to be in set starting point mode.
        /// </summary>
        public void ToggleSetStartingPoint()
        {
            if (_settingStartingPoint)
                _settingStartingPoint = false;
            else
                _settingStartingPoint = true;
        }

        #endregion
    }
}
